package net.luohuasheng.bee.proxy.mybatis.executor.criteria;

import net.luohuasheng.bee.proxy.mybatis.common.enums.FillterType;
import org.apache.commons.lang3.math.NumberUtils;

import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

import static net.luohuasheng.bee.proxy.mybatis.executor.criteria.CriteriaBuilder.NUM_SPLIT;

/**
 * @author luohuasheng
 */
public class Criteria extends HashMap<String, Object> {

    private final String name;
    private final Set<String> fields = new LinkedHashSet<>();


    private final List<CriteriaBuilder.CriteriaCondition> conditions;

    public Criteria(String name, List<CriteriaBuilder.CriteriaCondition> conditions, Set<String> fields) {
        super(0);
        this.name = name;
        this.conditions = conditions;
        this.fields.addAll(fields);
    }

    public List<CriteriaBuilder.CriteriaCondition> getConditions() {
        return conditions;
    }

    public Set<String> getFields() {
        return fields;
    }

    public String getSqlSegment() {
        StringBuilder sql = new StringBuilder();
        if (conditions != null) {
            if (conditions.size() == 1) {
                sql.append(createSubSql(conditions.get(0).getSubs()));
            } else {
                for (CriteriaBuilder.CriteriaCondition condition : conditions) {
                    String subSql = createSubSql(condition.getSubs());
                    if (subSql.length() == 0) {
                        continue;
                    }
                    if (sql.length() == 0) {
                        sql.append("(").append(subSql).append(")");
                    } else {
                        sql.append(condition.getConnectType().getContent()).append("(").append(subSql).append(")");
                    }

                }
            }
        }

        return sql.toString();
    }

    private String createSubSql(List<CriteriaBuilder.SubCriteriaCondition> subs) {
        StringBuilder sql = new StringBuilder();
        for (CriteriaBuilder.SubCriteriaCondition sub : subs) {
            String fragment = createFragment(sub.getFillterType(), sub.getColumn(), sub.isExpression());
            if (fragment.length() == 0) {
                continue;
            }
            if (sql.length() == 0) {
                sql.append(fragment);
            } else {
                sql.append(sub.getConnectType().getContent()).append(fragment);
            }
        }
        return sql.toString();
    }


    private String createFragment(FillterType fillterType, String column, boolean expression) {

        StringBuilder sql = new StringBuilder();
        switch (fillterType) {
            case NONE:
                sql.append(column);
                break;
            case START_WITH:
            case END_WITH:
            case CONTAIN:
                sql.append(getColumnName(column)).append(" like ").append(buildExpressionField(expression, column));
                break;
            case NOT_START_WITH:
            case NOT_CONTAIN:
            case NOT_END_WITH:
                sql.append(getColumnName(column)).append(" not like ").append(buildExpressionField(expression, column));
                break;
            case BETWEEN:
                sql.append(getColumnName(column)).append(" between ").append("#{").append(name).append(".").append(column).append(".start} and ").append("#{").append(name).append(".").append(column).append(".end}");
                break;
            case NOT_BETWEEN:
                sql.append(getColumnName(column)).append(" not between ").append("#{").append(name).append(".").append(column).append(".start} and ").append("#{").append(name).append(".").append(column).append(".end}");
                break;
            case IS_BLANK:
            case EQUALS:
                sql.append(getColumnName(column)).append(" = ").append(buildExpressionField(expression, column));
                break;
            case IS_NOT_BLANK:
            case NOT_EQUALS:
                sql.append(getColumnName(column)).append(" &lt;&gt; ").append(buildExpressionField(expression, column));
                break;
            case GREATER:
                sql.append(getColumnName(column)).append(" &gt; ").append(buildExpressionField(expression, column));
                ;
                break;
            case NOT_GREATER:
                sql.append(getColumnName(column)).append(" &lt;= ").append(buildExpressionField(expression, column));
                break;
            case LESS:
                sql.append(getColumnName(column)).append(" &lt; ").append(buildExpressionField(expression, column));
                break;
            case NOT_LESS:
                sql.append(getColumnName(column)).append(" &gt;= ").append(buildExpressionField(expression, column));
                break;
            case IS_NULL:
                sql.append(column).append(" is null ");
                break;
            case IS_NOT_NULL:
                sql.append(column).append(" is not null ");
                break;
            case IN:
                sql.append(getColumnName(column)).append(" in <foreach item=\"item\" collection=\"").append(column).append("\" separator=\",\" open=\"(\" close=\")\" >#{item}</foreach>");
                break;
            case NOT_IN:
                sql.append(getColumnName(column)).append(" not in <foreach item=\"item\" collection=\"").append(column).append("\" separator=\",\" open=\"(\" close=\")\" >#{item}</foreach>");
                break;
            case EXISTS:
                sql.append(" exists (").append(column).append(")");
                break;
            case NOT_EXISTS:
                sql.append(" not exists (").append(column).append(")");
                break;
            default:
                throw new IllegalStateException("Unexpected value: " + fillterType);
        }
        return sql.toString();
    }

    private String getColumnName(String column) {
        if (column.contains(NUM_SPLIT)) {
            String num = column.substring(column.lastIndexOf(NUM_SPLIT) + 1);
            if (NumberUtils.isCreatable(num)) {
                column = column.substring(0, column.lastIndexOf(NUM_SPLIT));
            }
        }
        return column;
    }

    private String buildExpressionField(boolean expression, String column) {
        return expression ? String.valueOf(getOrDefault(column, "null")) : "#{" + name + "." + column + "}";
    }

}
